package erfgame.core.world.terrain.factory;

import java.util.ArrayList;
import java.util.List;


import erfgame.core.Pair;
import erfgame.core.Point3D;
import erfgame.core.world.terrain.TerrainUpdate;
import erfgame.core.world.terrain.TerrainUpdateByMaterialTypeFactory;
import erfgame.core.world.terrain.TerrainUpdateFactory;
import erfgame.core.world.terrain.TerrainUpdateGroup;

public class IndexedTerrainUpdateByMaterialTypeFactory implements
		TerrainUpdateByMaterialTypeFactory {
	
	private byte[] index;
	private int dynamicIndex;
	private TerrainUpdateFactory indexedUpdateFactory;
	
	public IndexedTerrainUpdateByMaterialTypeFactory(
			TerrainUpdateFactory indexedUpdateFactory,
			byte[] index, 
			int dynamicIndex
	) {
		this.indexedUpdateFactory = indexedUpdateFactory;
		this.index = index;
		this.dynamicIndex = dynamicIndex;
	}

	public TerrainUpdateGroup createTerrainUpdate(byte materialType) {
		TerrainUpdateGroup indexedUpdateGroup = indexedUpdateFactory.createTerrainUpdate();
		List<Pair<Point3D, TerrainUpdate>> updates = indexedUpdateGroup.getTerrainUpdates();
		ArrayList<Pair<Point3D, TerrainUpdate>> newUpdates = new ArrayList<Pair<Point3D, TerrainUpdate>>( updates.size() );
		if( dynamicIndex >= 0 ) {
			index[dynamicIndex] = materialType;
		}
		
		for( int i=updates.size(); i>0; ) {
			i--;
			Pair<Point3D, TerrainUpdate> pair = updates.get( i );
			Point3D position = pair.getFirst();
			TerrainUpdate indexedUpdate = pair.getSecond();
			TerrainUpdate newUpdate = createTerrainUpdate( indexedUpdate );
			newUpdates.add(new Pair<Point3D, TerrainUpdate>( position, newUpdate ) );
		}
		return new TerrainUpdateGroup( newUpdates );
	}
		
	protected TerrainUpdate createTerrainUpdate( TerrainUpdate indexedUpdate ) {
			
		int width = indexedUpdate.getWidth();
		int height = indexedUpdate.getHeight();
		int depth = indexedUpdate.getDepth();
		
		short[][][] terrain = indexedUpdate.getTerrain();
		
		short[][][] resultTerrain = new short[width][height][depth];
		
		for( int x=width; x>0; ) {
			x--;
			short[][] terrainx = terrain[x];
			short[][] resultTerrainx = resultTerrain[x];
			for( int y=height; y>0; ) {
				y--;
				short[] terrainxy = terrainx[y];
				short[] resultTerrainxy = resultTerrainx[y];
				for( int z=depth; z>0; ) {
					z--;
					short t = terrainxy[z]; 
					int i = t & 0xff;
					int flag = t & 0xff00;
					byte terrainType = index[i];
					resultTerrainxy[z] = (short)(terrainType | flag);
				}
			}
		}
		// TODO if we have indexes to the same terrain type
		return new TerrainUpdate(
				resultTerrain, 
				width, height, depth, 
				indexedUpdate.getSummary()
		);
	}

}
